/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: string.h
 *
 * Purpose: This section describes the ara::core::String type and its complement
 *          ara::core::BasicString which both represent sequences of characters
 *
 * Developer:
 *   wen.gu , 2020-04-17
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef __ARA_CORE_STRING_H__
#define __ARA_CORE_STRING_H__
/******************************************************************************
 **    INCLUDES
 ******************************************************************************/

#include <type_traits>
#include <string>

#include "ara/core/string_view.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace ara
{
namespace core
{
/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/
#if 0
/** todo, implementation-defined for Allocator, now workaround as std::allocator */
template<class Allocator = std::allocator<char> >
class BasicString
{
public:
    using base = std::basic_string<char, std::char_traits<char>, Allocator>;

    using traits_type = typename base::traits_type;
    using allocator_type = typename base::allocator_type;

    using value_type = typename base::value_type;
    using size_type = typename base::size_type;
    using difference_type = typename base::difference_type;
    using pointer = typename base::pointer;
    using const_pointer = typename base::const_pointer;
    using reference = value_type&;
    using const_reference = const value_type&;

    using iterator = typename base::iterator;
    using const_iterator = typename base::const_iterator;

    using reverse_iterator = typename base::reverse_iterator;
    using const_reverse_iterator = typename base::const_reverse_iterator;

public:
    static const size_type npos = -1;

public:
    // 21.4.2, construct/copy/destroy:
    BasicString() noexcept : BasicString(Allocator()) { }
    explicit BasicString(const Allocator& a) noexcept
        :mStr(a)
    {
        /** todo something*/
    }

    BasicString(const BasicString& str)
        :mStr(str.mStr)
    {
        /** todo something*/
    }

    BasicString(BasicString&& str) noexcept
        :mStr(std::move(str.mStr))
    {
        /** todo something*/
    }

    BasicString(const BasicString& str, size_type pos, size_type n = npos, const Allocator& a = Allocator())
        :mStr(str.mStr, pos, n, a)
    {
        /** todo something*/
    }

    BasicString(const value_type* s, size_type n, const Allocator& a = Allocator())
        :mStr(s, n, a)
    {
        /** todo something*/
    }

    BasicString(const value_type* s, const Allocator& a = Allocator())
        :mStr(s, a)
    {
        /** todo something*/
    }

    BasicString(size_type n, value_type c, const Allocator& a = Allocator())
        :mStr(n, c, a)
    {
        /** todo something*/
    }

    template<class InputIterator>
    BasicString(InputIterator begin, InputIterator end, const Allocator& a = Allocator())
        : mStr(begin, end, a)
    {
        /** todo something*/
    }


    BasicString(std::initializer_list<value_type> il, const Allocator& a = Allocator())
        :mStr(il, a)
    {
        /** todo something*/
    }

    BasicString(const BasicString& other, const Allocator& a)
        :mStr(other.mStr, a)
    {
        /** todo something*/
    }

    BasicString(BasicString&& other, const Allocator& a)
        :mStr(std::move(other.mStr), a)
    {
        /** todo something*/
    }

    ~BasicString()
    {
        /** todo something */
    }

public:
    BasicString& operator=(const BasicString& str)
    {
        mStr = str.mStr;
        return *this;

    }

    BasicString& operator=(BasicString&& str) noexcept(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
        std::allocator_traits<Allocator>::is_always_equal::value)
    {
        mStr = std::move(str.mStr);
        return *this;
    }
    BasicString& operator=(const value_type* s)
    {
        mStr = s;
        return *this;
    }

    BasicString& operator=(value_type c)
    {
        mStr = c;
        return *this;
    }

    BasicString& operator=(std::initializer_list<value_type> il)
    {
        mStr = il;
        return *this;
    }

public:
    // 21.4.3, iterators:
    iterator begin() noexcept
    {
        return mStr.begin();
    }

    const_iterator begin() const noexcept
    {
        return mStr.begin();
    }

    iterator end() noexcept
    {
        return mStr.end();
    }

    const_iterator end() const noexcept
    {
        return mStr.end();
    }

    reverse_iterator rbegin() noexcept
    {
        return mStr.rbegin();
    }

    const_reverse_iterator rbegin() const noexcept
    {
        return mStr.rbegin();
    }

    reverse_iterator rend() noexcept
    {
        return mStr.rend();
    }

    const_reverse_iterator rend() const noexcept
    {
        return mStr.rend();
    }

    const_iterator cbegin() const noexcept
    {
        return mStr.cbegin();
    }

    const_iterator cend() const noexcept
    {
        return mStr.cend();
    }

    const_reverse_iterator crbegin() const noexcept
    {
        return mStr.crbegin();
    }

    const_reverse_iterator crend() const noexcept
    {
        return mStr.crend();
    }

public:
    // 21.4.4, capacity:
    size_type size() const noexcept
    {
        return mStr.size();
    }
    size_type length() const noexcept
    {
        return mStr.length();
    }

    size_type max_size() const noexcept
    {
        return mStr.max_size();
    }

    void resize(size_type n, value_type c)
    {
        mStr.resize(n, c);
    }

    void resize(size_type n)
    {
        mStr.resize(n);
    }

    size_type capacity() const noexcept
    {
        return mStr.capacity();
    }

    void reserve(size_type res_arg = 0)
    {
        mStr.reserve(res_arg);
    }

    void shrink_to_fit()
    {
        mStr.shrink_to_fit();
    }

    void clear() noexcept
    {
        mStr.clear();
    }

    bool empty() const noexcept
    {
        return mStr.empty();
    }

public:
    // 21.4.5, element access:
    const_reference operator[](size_type pos) const
    {
        return mStr[pos];
    }

    reference operator[](size_type pos)
    {
        return mStr[pos];
    }

    const_reference at(size_type n) const
    {
        return mStr.at(n);
    }

    reference at(size_type n)
    {
        return mStr.at(n);
    }

    const value_type& front() const
    {
        return mStr.front();
    }

    value_type& front()
    {
        return mStr.front();
    }

    const value_type& back() const
    {
        return mStr.back();
    }

    value_type& back()
    {
        return mStr.back();
    }
public:
    // 21.4.6, modifiers:
    BasicString& operator+=(const BasicString& str)
    {
        mStr += str.mStr;
        return *this;
    }

    BasicString& operator+=(const value_type* s)
    {
        mStr += s;
        return *this;
    }

    BasicString& operator+=(value_type c)
    {
        mStr += c;
        return *this;
    }

    BasicString& operator+=(std::initializer_list<value_type> il)
    {
        mStr += il;
        return *this;
    }

    BasicString& append(const BasicString& str)
    {
        mStr.append(str.mStr);
        return *this;
    }

    BasicString& append(const BasicString& str, size_type pos, size_type n = npos)
    {
        mStr.append(str.mStr, pos, n);
        return *this;
    }

    BasicString& append(const value_type* s, size_type n)
    {
        mStr.append(s, n);
        return *this;
    }

    BasicString& append(const value_type* s)
    {
        mStr.append(s);
        return *this;
    }
#if 0
    BasicString& append(size_type n, value_type c)
    {
        mStr.append(n, c);
        return *this;
    }
#endif
    template<class InputIterator>
    BasicString& append(InputIterator first, InputIterator last)
    {
        mStr.append(first, last);
        return *this;
    }

    BasicString& append(std::initializer_list<value_type> il)
    {
        mStr.append(il);
        return *this;
    }

    void push_back(value_type c)
    {
        mStr.push_back(c);
    }

    BasicString& assign(const BasicString& str)
    {
        mStr.assign(str.mStr);
        return *this;
    }

    BasicString& assign(BasicString&& str) noexcept
    {
        mStr.assign(std::move(str.mStr));
        return *this;
    }

    BasicString& assign(const BasicString& str, size_type pos, size_type n = npos)
    {
        mStr.assign(str.mStr, pos, n);
        return *this;
    }

    BasicString& assign(const value_type* s, size_type n)
    {
        mStr.assign(s, n);
        return *this;
    }

    BasicString& assign(const value_type* s)
    {
        mStr.assign(s);
        return *this;
    }

    BasicString& assign(size_type n, value_type c)
    {
        mStr.assign(n, c);
        return *this;
    }

    template<class InputIterator>
    BasicString& assign(InputIterator first, InputIterator last)
    {
        mStr.assign(first, last);
        return *this;
    }

    BasicString& assign(std::initializer_list<value_type> il)
    {
        mStr.assign(il);
        return *this;
    }

    BasicString& insert(size_type pos1, const BasicString& str)
    {
        mStr.insert(pos1, str.mStr);
        return *this;
    }

    BasicString& insert(size_type pos1, const BasicString& str, size_type pos2, size_type n = npos)
    {
        mStr.insert(pos1, str.mStr, pos2, n);
        return *this;
    }

    BasicString& insert(size_type pos, const value_type* s, size_type n)
    {
        mStr.insert(pos, s, n);
        return *this;
    }

    BasicString& insert(size_type pos, const value_type* s)
    {
        mStr.insert(pos, s);
        return *this;
    }

    BasicString& insert(size_type pos, size_type n, value_type c)
    {
        mStr.insert(pos, n, c);
        return *this;
    }

    iterator insert(const_iterator p, value_type c)
    {
        mStr.insert(p, c);
        return *this;
    }

    iterator insert(const_iterator p, size_type n, value_type c)
    {
        mStr.insert(p, n, c);
        return *this;
    }

    template<class InputIterator>
    iterator insert(const_iterator p, InputIterator first, InputIterator last)
    {
        mStr.insert(p, first, last);
        return *this;
    }

    iterator insert(const_iterator p, std::initializer_list<value_type> il)
    {
        mStr.insert(p, il);
        return *this;
    }

    BasicString& erase(size_type pos = 0, size_type n = npos)
    {
        mStr.erase(pos, n);
        return *this;
    }

    iterator erase(const_iterator p)
    {
        return mStr.erase(p);
        //return *this;
    }

    iterator erase(const_iterator first, const_iterator last)
    {
        return mStr.erase(first, last);
        //return *this;
    }

    void pop_back()
    {
        mStr.pop_back();
    }

    BasicString& replace(size_type pos1, size_type n1, const BasicString& str)
    {
        mStr.replace(pos1, n1, str.mStr);
        return *this;
    }

    BasicString& replace(size_type pos1, size_type n1, const BasicString& str, size_type pos2, size_type n2 = npos)
    {
        mStr.replace(pos1, n1, str.mStr, pos2, n2);
        return *this;
    }

    BasicString& replace(size_type pos, size_type n1, const value_type* s, size_type n2)
    {
        mStr.replace(pos, n1, s, n2);
        return *this;
    }

    BasicString& replace(size_type pos, size_type n1, const value_type* s)
    {
        mStr.replace(pos, n1, s);
        return *this;
    }

    BasicString& replace(size_type pos, size_type n1, size_type n2, value_type c)
    {
        mStr.replace(pos, n1, n2, c);
        return *this;
    }

    BasicString& replace(const_iterator i1, const_iterator i2, const BasicString& str)
    {
        mStr.replace(i1, i2, str.mStr);
        return *this;
    }

    BasicString& replace(const_iterator i1, const_iterator i2, const value_type* s, size_type n)
    {
        mStr.replace(i1, i2, s, n);
        return *this;
    }

    BasicString& replace(const_iterator i1, const_iterator i2, const value_type* s)
    {
        mStr.replace(i1, i2, s);
        return *this;
    }

    BasicString& replace(const_iterator i1, const_iterator i2, size_type n, value_type c)
    {
        mStr.replace(i1, i2, n, c);
        return *this;
    }

    template<class InputIterator>
    BasicString& replace(const_iterator i1, const_iterator i2, InputIterator j1, InputIterator j2)
    {
        mStr.replace(i1, i2, j1, j2);
        return *this;
    }

    BasicString& replace(const_iterator i1, const_iterator i2, std::initializer_list<value_type> il)
    {
        mStr.replace(i1, i2, il);
        return *this;
    }

    size_type copy(value_type* s, size_type n, size_type pos = 0) const
    {
        return mStr.copy(s, n, pos);
    }

    void swap(BasicString& str) noexcept(std::allocator_traits<Allocator>::propagate_on_container_swap::value ||
        std::allocator_traits<Allocator>::is_always_equal::value)
    {
        mStr.swap(str.mStr);
    }

public:
    // 21.4.7, string operations:
    const value_type* c_str() const noexcept
    {
        return mStr.c_str();
    }

    const value_type* data() const noexcept
    {
        return mStr.data();
    }

    allocator_type get_allocator() const noexcept
    {
        return mStr.get_allocator();
    }

    size_type find(const BasicString& str, size_type pos = 0) const noexcept
    {
        return mStr.find(str.mStr, pos);
    }

    size_type find(const value_type* s, size_type pos, size_type n) const
    {
        return mStr.find(s, pos, n);
    }

    size_type find(const value_type* s, size_type pos = 0) const
    {
        return mStr.find(s, pos);
    }

    size_type find(value_type c, size_type pos = 0) const
    {
        return mStr.find(c, pos);
    }

    size_type rfind(const BasicString& str, size_type pos = npos) const noexcept
    {
        return mStr.rfind(str.mStr, pos);
    }

    size_type rfind(const value_type* s, size_type pos, size_type n) const
    {
        return mStr.rfind(s, pos, n);
    }

    size_type rfind(const value_type* s, size_type pos = npos) const
    {
        return mStr.rfind(s, pos);
    }

    size_type rfind(value_type c, size_type pos = npos) const
    {
        return mStr.rfind(c, pos);
    }

    size_type find_first_of(const BasicString& str, size_type pos = 0) const noexcept
    {
        return mStr.find_first_of(str.mStr, pos);
    }

    size_type find_first_of(const value_type* s, size_type pos, size_type n) const
    {
        return mStr.find_first_of(s, pos, n);
    }

    size_type find_first_of(const value_type* s, size_type pos = 0) const
    {
        return mStr.find_first_of(s, pos);
    }

    size_type find_first_of(value_type c, size_type pos = 0) const
    {
        return mStr.find_first_of(c, pos);
    }

    size_type find_last_of(const BasicString& str, size_type pos = npos) const noexcept
    {
        return mStr.find_last_of(str.mStr, pos);
    }

    size_type find_last_of(const value_type* s, size_type pos, size_type n) const
    {
        return mStr.find_last_of(s, pos, n);
    }

    size_type find_last_of(const value_type* s, size_type pos = npos) const
    {
        return mStr.find_last_of(s, pos);
    }

    size_type find_last_of(value_type c, size_type pos = npos) const
    {
        return mStr.find_last_of(c, pos);
    }

    size_type find_first_not_of(const BasicString& str, size_type pos = 0) const noexcept
    {
        return mStr.find_first_not_of(str.mStr, pos);
    }

    size_type find_first_not_of(const value_type* s, size_type pos, size_type n) const
    {
        return mStr.find_first_not_of(s, pos, n);
    }

    size_type find_first_not_of(const value_type* s, size_type pos = 0) const
    {
        return mStr.find_first_not_of(s, pos);
    }

    size_type find_first_not_of(value_type c, size_type pos = 0) const
    {
        return mStr.find_first_not_of(c, pos);
    }

    size_type find_last_not_of(const BasicString& str, size_type pos = npos) const noexcept
    {
        return mStr.find_last_not_of(str.mStr, pos);
    }

    size_type find_last_not_of(const value_type* s, size_type pos, size_type n) const
    {
        return mStr.find_last_not_of(s, pos, n);
    }

    size_type find_last_not_of(const value_type* s, size_type pos = npos) const
    {
        return mStr.find_last_not_of(s, pos);
    }

    size_type find_last_not_of(value_type c, size_type pos = npos) const
    {
        return mStr.find_last_not_of(c, pos);
    }

public:
    BasicString substr(size_type pos = 0, size_type n = npos) const
    {
        return BasicString(mStr.substr(pos, n));
    }

    int compare(const BasicString& str) const noexcept
    {
        return mStr.compare(str.mStr);
    }

    int compare(size_type pos1, size_type n1, const BasicString& str) const
    {
        return mStr.compare(pos1, n1, str.mStr);
    }

    int compare(size_type pos1, size_type n1, const BasicString& str, size_type pos2, size_type n2 = npos) const
    {
        return mStr.compare(pos1, n1, str.mStr, pos2, n2);
    }

    int compare(const value_type* s) const
    {
        return mStr.compare(s);
    }

    int compare(size_type pos1, size_type n1, const value_type* s) const
    {
        return mStr.compare(pos1, n1, s);
    }

    int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const
    {
        return mStr.compare(pos1, n1, s, n2);
    }

public:
    explicit BasicString(StringView sv)
        :mStr(sv.data(), sv.size())
    {
        /** todo something */
    }

    template <typename T>
    BasicString(T const& t, size_type pos, size_type n)
        : BasicString(StringView(t).substr(pos, n))
    {

    }

public:
    operator StringView() const noexcept
    {
        return StringView(data(), size());
    }

    BasicString& operator=(StringView sv)
    {
        return assign(sv);
    }

    BasicString& assign(StringView sv)
    {
        mStr.assign(sv.data(), sv.size());
        return *this;
    }

    template <typename T>
    BasicString& assign(T const& t, size_type pos, size_type n = npos)
    {
        StringView sv = t;
        if (pos > sv.size())
        {
            throw std::out_of_range("assign out of range");
        }

        size_t leftSize = sv.size() - pos;

        mStr.assign(sv.data() + pos, (n < leftSize) ? n : leftSize);
        return *this;
    }


    BasicString& operator+=(StringView sv)
    {
        return append(sv);
    }

    BasicString& append(StringView sv)
    {
        mStr.append(sv.data(), sv.size());
        return *this;
    }

    template <typename T>
    BasicString& append(T const& t, size_type pos, size_type n = npos)
    {
        StringView sv = t;
        if (pos > sv.size())
        {
            throw std::out_of_range("append out of range");
        }
        size_t leftSize = sv.size() - pos;

        mStr.append(sv.data() + pos, (n < leftSize) ? n : leftSize);

        return *this;
    }

    BasicString& insert(size_type pos, StringView sv)
    {
        mStr.insert(pos, sv.data(), sv.size());
        return *this;
    }


    template <typename T>
    BasicString& insert(size_type pos1, T const& t, size_type pos2, size_type n = npos)
    {
        StringView sv = t;
        if ((pos1 > size()) || (pos2 > sv.size()))
        {
            throw std::out_of_range("insert out of range");
        }
        size_t leftSize = sv.size() - pos2;

        mStr.insert(pos1, sv.data() + pos2, (n < leftSize) ? n : leftSize);
        return *this;
    }

    BasicString& replace(size_type pos1, size_type n1, StringView sv)
    {
        mStr.replace(pos1, n1, sv.data(), sv.size());
        return *this;
    }

    template <typename T>
    BasicString& replace(size_type pos1, size_type n1, T const& t, size_type pos2, size_type n2 = npos)
    {
        StringView sv = t;
        if ((pos1 > size()) || (pos2 > sv.size()))
        {
            throw std::out_of_range("insert out of range");
        }
        size_t leftSize = sv.size() - pos2;

        mStr.replace(pos1, n1, sv.data() + pos2, (n2 < leftSize) ? n2 : leftSize);
        return *this;
    }

    BasicString& replace(const_iterator i1, const_iterator i2, StringView sv)
    {
        return replace(i1 - begin(), i2 - i1, sv);
    }

    size_type find(StringView sv, size_type pos = 0) const noexcept
    {
        return mStr.find(sv.data(), pos, sv.size());
    }

    size_type rfind(StringView sv, size_type pos = npos) const noexcept
    {
        return mStr.rfind(sv.data(), pos, sv.size());
    }

    size_type find_first_of(StringView sv, size_type pos = 0) const noexcept
    {
        return mStr.find_first_of(sv.data(), pos, sv.size());
    }

    size_type find_last_of(StringView sv, size_type pos = npos) const noexcept
    {
        return mStr.find_last_of(sv.data(), pos, sv.size());
    }

    size_type find_first_not_of(StringView sv, size_type pos = 0) const noexcept
    {
        return mStr.find_first_not_of(sv.data(), pos, sv.size());
    }

    size_type find_last_not_of(StringView sv, size_type pos = npos) const noexcept
    {
        return mStr.find_last_not_of(sv.data(), pos, sv.size());
    }

    int compare(StringView sv) const noexcept
    {
        return compare(0, size(), sv.data(), sv.size());
    }

    int compare(size_type pos1, size_type n1, StringView sv) const
    {
        return compare(pos1, n1, sv.data(), sv.size());
    }

    template <typename T>
    int compare(size_type pos1, size_type n1, T const& t, size_type pos2, size_type n2 = npos) const
    {
        StringView sv = t;
        if ((pos1 > size()) || (pos2 > sv.size()))
        {
            throw std::out_of_range("insert out of range");
        }

        size_t leftSize = sv.size() - pos2;

        return compare(pos1, n1, sv.data(), (n2 < leftSize) ? n2 : leftSize);
    }

public: /** about std::string */
    BasicString(const std::string& other)
        :mStr(other)
    {
        /** todo, something */
    }

    BasicString(std::string&& other)
        :mStr(std::move(other))
    {
        /**todo, something */
    }

    BasicString& operator=(const std::string& other)
    {
        mStr = other;
        return *this;
    }
    BasicString& operator=(std::string&& other)
    {
        mStr = std::move(other);
        return *this;
    }

    const std::string& StdString() const
    {
        return mStr;
    }
private:
    base mStr;
};

using String = BasicString<>;

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs)
{
    BasicString<Allocator>  r = lhs;
    r.append(rhs);
    return r;
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator>operator+(const BasicString<Allocator>& lhs, const char* rhs)
{
    BasicString<Allocator>  r = lhs;
    r.append(rhs);
    return r;
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(BasicString<Allocator>&& lhs, const BasicString<Allocator>& rhs)
{
    lhs.append(rhs);
    return std::move(lhs);
}


template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(BasicString<Allocator>&& lhs, const char* rhs)
{
    lhs.append(rhs);
    return std::move(lhs);
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(BasicString<Allocator>&& lhs, BasicString<Allocator>&& rhs)
{
    lhs.append(rhs);
    return std::move(lhs);
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(const BasicString<Allocator>& lhs, BasicString<Allocator>&& rhs)
{
    rhs.insert(0, lhs);
    return std::move(rhs);
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(const char* lhs, BasicString<Allocator>&& rhs)
{
    rhs.insert(0, lhs);
    return std::move(rhs);

}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(const char* lhs, const BasicString<Allocator>& rhs)
{
    BasicString<Allocator> r = rhs;
    r.insert(0, lhs);
    return r;    
}


template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(char lhs, const BasicString<Allocator>& rhs)
{
    BasicString<Allocator> r = rhs;
    r.insert(r.begin(), lhs);
    return r;
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(char lhs, BasicString<Allocator>&& rhs)
{
    rhs.insert(rhs.begin(), lhs);
    return std::move(rhs);
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(const BasicString<Allocator>& lhs, char rhs)
{
    BasicString<Allocator> r = lhs;
    r.push_back(rhs);
    return r;
}

template<class Allocator = std::allocator<char>>
BasicString<Allocator> operator+(BasicString<Allocator>&& lhs, char rhs)
{
    lhs.push_back(rhs);
    return std::move(lhs);
}

template<class Allocator = std::allocator<char>>
bool operator==(const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs) noexcept
{
    return BasicStringView<char>(lhs) == BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator==(const char* lhs, const BasicString<Allocator>& rhs)
{
    return BasicStringView<char>(lhs) == BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator==(const BasicString<Allocator>& lhs, const char* rhs)
{
    return BasicStringView<char>(lhs) == BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator!=(const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs) noexcept
{
    return BasicStringView<char>(lhs) != BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator!=(const char* lhs, const BasicString<Allocator>& rhs)
{
    return BasicStringView<char>(lhs) != BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator!=(const BasicString<Allocator>& lhs, const char* rhs)
{
    return BasicStringView<char>(lhs) != BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator< (const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs) noexcept
{
    return BasicStringView<char>(lhs) < BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator< (const char* lhs, const BasicString<Allocator>& rhs)
{
    return BasicStringView<char>(lhs) < BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator< (const BasicString<Allocator>& lhs, const char* rhs)
{
    return BasicStringView<char>(lhs) < BasicStringView<char>(rhs);
}
template<class Allocator = std::allocator<char>>
bool operator> (const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs) noexcept
{
    return BasicStringView<char>(lhs) > BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator> (const char* lhs, const BasicString<Allocator>& rhs)
{
    return BasicStringView<char>(lhs) > BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator> (const BasicString<Allocator>& lhs, const char* rhs)
{
    return BasicStringView<char>(lhs) > BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator<=(const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs) noexcept
{
    return BasicStringView<char>(lhs) <= BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator<=(const char* lhs, const BasicString<Allocator>& rhs)
{
    return BasicStringView<char>(lhs) <= BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator<=(const BasicString<Allocator>& lhs, const char* rhs)
{
    return BasicStringView<char>(lhs) <= BasicStringView<char>(rhs);
}
template<class Allocator = std::allocator<char>>
bool operator>=(const BasicString<Allocator>& lhs, const BasicString<Allocator>& rhs) noexcept
{
    return BasicStringView<char>(lhs) >= BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator>=(const char* lhs, const BasicString<Allocator>& rhs)
{
    return BasicStringView<char>(lhs) >= BasicStringView<char>(rhs);
}

template<class Allocator = std::allocator<char>>
bool operator>=(const BasicString<Allocator>& lhs, const char* rhs)
{
    return BasicStringView<char>(lhs) >= BasicStringView<char>(rhs);
}



template<class Allocator = std::allocator<char>>
void swap(BasicString<Allocator>& lhs, BasicString<Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
    lhs.swap(rhs);
}

#if 1
template<class Allocator = std::allocator<char>>
std::basic_istream<char>& operator>>(std::basic_istream<char>& is, BasicString<Allocator>& str)
{
    std::string& sstr = str.StdString();
    is >> sstr;
    return  is;
}

template<class Allocator = std::allocator<char>>
std::basic_ostream<char>& operator<<(std::basic_ostream<char>& os, const BasicString<Allocator>& str)
{
    const std::string& sStr = str.StdString();
    os << sStr;
    return os;
}

template<class Allocator = std::allocator<char>>
std::basic_istream<char>& getline(std::basic_istream<char>& is, BasicString<Allocator>& str, char delim)
{
    std::getline(is, str.StdString(), delim);
    return is;
}

template<class Allocator = std::allocator<char> >
std::basic_istream<char>& getline(std::basic_istream<char>&& is, BasicString<Allocator>& str, char delim)
{
    std::getline(std::forward< std::basic_istream<char> >(is), str.StdString(), delim);
    return is;
}

template<class Allocator = std::allocator<char>>
std::basic_istream<char>& getline(std::basic_istream<char>& is, BasicString<Allocator>& str)
{
    std::getline(is, str.StdString());
    return is;
}

template<class Allocator = std::allocator<char>>
std::basic_istream<char>& getline(std::basic_istream<char>&& is, BasicString<Allocator>& str)
{
    std::getline(std::forward< std::basic_istream<char> >(is), str.StdString());
    return is;
}

#endif
template<> struct hash<String>;


String operator""S(const char* str, size_t len)
{
    return String{ str,len };
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#else
template<class Allocator = std::allocator<char> >
class BasicString : public std::basic_string<char,std::char_traits<char>, Allocator>
{
public: 
    using base = std::basic_string<char, std::char_traits<char>, Allocator>;

    using traits_type = base::traits_type;
    using allocator_type = base::allocator_type;

    using value_type = base::value_type;
    using size_type = base::size_type;
    using difference_type = base::difference_type;
    using pointer = base::pointer;
    using const_pointer = base::const_pointer;
    using reference = value_type&;
    using const_reference = const value_type&;

    using iterator = base::iterator;
    using const_iterator = base::const_iterator;

    using reverse_iterator = base::reverse_iterator;
    using const_reverse_iterator = base::const_reverse_iterator;

public:
    template<typename _Tp, typename _Res>
    using IfStringView = typename std::enable_if<(std::is_convertible<const _Tp&, BasicStringView<char> >::value &&
        (!std::is_convertible<const _Tp*, const BasicString*>::value) &&
        (!std::is_convertible<const _Tp&, const char*>::value)),   _Res>::type;
    
public:
    /** using constructor from base class */
    using base::base;

    /** using operator override */
    using base::operator=;
    using base::operator+=;
    using base::operator[];


    /**using member funcations from base class */
    using base::begin;
    using base::end;
    using base::rbegin;
    using base::rend;
    using base::cbegin;
    using base::cend;
    using base::crbegin;
    using base::crend;
    using base::size;
    using base::length;
    using base::max_size;
    using base::resize;
    using base::capacity;
    using base::reserve;
    using base::shrink_to_fit;
    using base::clear;
    using base::empty;
    using base::at;
    using base::front;
    using base::back;
    using base::append;
    using base::assign;
    using base::insert;
    using base::erase;
    using base::replace;
    using base::copy;
    using base::swap;
    using base::c_str;
    using base::data;
    using base::get_allocator;
    using base::find;
    using base::rfind;
    using base::find_first_of;
    using base::find_last_of;
    using base::find_first_not_of;
    using base::find_last_not_of;
    using base::substr;
    using base::compare;

    /** using static member variable */
    using base::npos;
public: /**expend from std::basic_string */
    BasicString() = default;
    explicit BasicString(StringView sv)
        :base(sv.data(), sv.size())
    {
        /** todo something */
    }

    template <typename T>
    BasicString(T const& t, size_type pos, size_type n)
        :base(StringView(t).substr(pos, n))
    {

    }

public: /** todo support covert std::string*/

    BasicString(const base& other)
        :base(other)
    {
        /** todo something */
    }

    BasicString& operator=(const base& other)
    {
        *this = other;
        return *this;
    }

    operator std::string() const noexcept
    {
        return *this;
    }

public:


    operator StringView() const noexcept
    {
        return StringView(data(), size());
    }

    BasicString& operator=(StringView sv)
    {
        return assign(sv);
    }

    BasicString& assign(StringView sv)
    {
        return assign(sv.data(), sv.size());
    }

    template <typename T>
    IfStringView<T, BasicString&> assign(T const& t, size_type pos, size_type n = npos)
    {
        StringView sv = t;
        if (pos > sv.size())
        {
            throw std::out_of_range("assign out of range");
        }

        size_t leftSize = sv.size() - pos;

        return assign(sv.data() + pos, (n < leftSize) ? n : leftSize);
    }


    BasicString& operator+=(StringView sv)
    {
        return append(sv);
    }

    BasicString& append(StringView sv)
    {
        return append(sv.data(), sv.size());
    }

    template <typename T>
    IfStringView<T, BasicString&> append(T const& t, size_type pos, size_type n = npos)
    {
        StringView sv = t;
        if (pos > sv.size())
        {
            throw std::out_of_range("append out of range");
        }
        size_t leftSize = sv.size() - pos;

        append(sv.data() + pos, (n < leftSize) ? n : leftSize);

        return *this;
    }


    BasicString& insert(size_type pos, StringView sv)
    {
        return insert(pos, sv.data(), sv.size());
    }


    template <typename T>
    IfStringView<T, BasicString&> insert(size_type pos1, T const& t, size_type pos2, size_type n = npos)
    {
        StringView sv = t;
        if ((pos1 > size()) || (pos2 > sv.size()))
        {
            throw std::out_of_range("insert out of range");
        }
        size_t leftSize = sv.size() - pos2;

        return insert(pos1, sv.data() + pos2, (n < leftSize) ? n : leftSize);
    }

    BasicString& replace(size_type pos1, size_type n1, StringView sv)
    {
        return replace(pos1, n1, sv.data(), sv.size());
    }

    template <typename T>
    IfStringView<T, BasicString&> replace(size_type pos1, size_type n1, T const& t, size_type pos2, size_type n2 = npos)
    {
         StringView sv = t;
        if ((pos1 > size()) || (pos2 > sv.size()))
        {
            throw std::out_of_range("insert out of range");
        }
        size_t leftSize = sv.size() - pos2;

        return replace(pos1, n1, sv.data() + pos2, (n2 < leftSize) ? n2 : leftSize);
    }

    BasicString& replace(const_iterator i1, const_iterator i2, StringView sv)
    {
        return replace(i1 - begin(), i2 - i1, sv);
    }

    size_type find(StringView sv, size_type pos = 0) const noexcept
    {
        return find(sv.data(), pos, sv.size());
    }

    size_type rfind(StringView sv, size_type pos = npos) const noexcept
    {
        return rfind(sv.data(), pos, sv.size());
    }

    size_type find_first_of(StringView sv, size_type pos = 0) const noexcept
    {
        return find_first_of(sv.data(), pos, sv.size());
    }

    size_type find_last_of(StringView sv, size_type pos = npos) const noexcept
    {
        return find_last_of(sv.data(), pos, sv.size());
    }

    size_type find_first_not_of(StringView sv, size_type pos = 0) const noexcept
    {
        return find_first_not_of(sv.data(), pos, sv.size());
    }

    size_type find_last_not_of(StringView sv, size_type pos = npos) const noexcept
    {
        return find_last_not_of(sv.data(), pos, sv.size());
    }

    int compare(StringView sv) const noexcept
    {
        return compare(BasicString(sv));
    }

    int compare(size_type pos1, size_type n1, StringView sv) const
    {
        return compare(pos1, n1, BasicString(sv));
    }

    template <typename T>
    IfStringView<T, int> compare(size_type pos1, size_type n1, T const& t, size_type pos2, size_type n2 = npos) const
    {
        StringView sv = t;
        if ((pos1 > size()) || (pos2 > sv.size()))
        {
            throw std::out_of_range("insert out of range");
        }

        size_t leftSize = sv.size() - pos2;

        return compare(pos1, n1, BasicString(sv.data(), (n2 < leftSize) ? n2 : leftSize));
    }

};


template <typename Allocator>
void swap(BasicString<Allocator>& lhs, BasicString<Allocator>& rhs)
{
    return lhs.swap(rhs);
}


using String = BasicString<>;


#endif
} /** namespace core */
} /** namespace ara */

#endif /** !__ARA_CORE_STRING_H__ */

